home *** CD-ROM | disk | FTP | other *** search
/ MacWorld 2003 August / MW 8 2003 CD1.iso / Inside Macworld / Product News / gimp-1.2.4.sit / gimp-1.2.4 / plug-ins / common / xpm.c < prev    next >
Encoding:
C/C++ Source or Header  |  2002-07-20  |  21.4 KB  |  873 lines

  1. /* The GIMP -- an image manipulation program
  2.  * Copyright (C) 1995 Spencer Kimball and Peter Mattis
  3.  *
  4.  * This program is free software; you can redistribute it and/or modify
  5.  * it under the terms of the GNU General Public License as published by
  6.  * the Free Software Foundation; either version 2 of the License, or
  7.  * (at your option) any later version.
  8.  *
  9.  * This program is distributed in the hope that it will be useful,
  10.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12.  * GNU General Public License for more details.
  13.  *
  14.  * You should have received a copy of the GNU General Public License
  15.  * along with this program; if not, write to the Free Software
  16.  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  17.  */
  18.  
  19. /* XPM plugin version 1.2.4 */
  20.  
  21. /*
  22. 1.2.4 displays an error message if saving fails (bug #87588)
  23.  
  24. 1.2.3 fixes bug when running in noninteractive mode
  25. changes alpha_threshold range from [0, 1] to [0,255] for consistency with
  26. the threshold_alpha plugin
  27.  
  28. 1.2.2 fixes bug that generated bad digits on images with more than 20000
  29. colors. (thanks, yanele)
  30. parses gtkrc (thanks, yosh)
  31. doesn't load parameter screen on images that don't have alpha
  32.  
  33. 1.2.1 fixes some minor bugs -- spaces in #XXXXXX strings, small typos in code.
  34.  
  35. 1.2 compute color indexes so that we don't have to use XpmSaveXImage*
  36.  
  37. Previous...Inherited code from Ray Lehtiniemi, who inherited it from S & P.
  38. */
  39.  
  40. #include "config.h"
  41.  
  42. #include <stdlib.h>
  43. #include <stdio.h>
  44. #include <string.h>
  45.  
  46. #include <gtk/gtk.h>
  47.  
  48. #ifdef GDK_WINDOWING_WIN32
  49. #ifndef XPM_NO_X
  50. #define XPM_NO_X
  51. #endif
  52. #else
  53. #include <X11/Xlib.h>
  54. #endif
  55.  
  56. #include <X11/xpm.h>
  57.  
  58. #include <libgimp/gimp.h>
  59. #include <libgimp/gimpui.h>
  60.  
  61. #include "libgimp/stdplugins-intl.h"
  62.  
  63.  
  64. static const gchar linenoise [] =
  65. " .+@#$%&*=-;>,')!~{]^/(_:<[}|1234567890abcdefghijklmnopqrstuvwxyz\
  66. ABCDEFGHIJKLMNOPQRSTUVWXYZ`";
  67.  
  68. #define SCALE_WIDTH 125
  69.  
  70. /* Structs for the save dialog */
  71. typedef struct
  72. {
  73.   gint threshold;
  74. } XpmSaveVals;
  75.  
  76. typedef struct
  77. {
  78.   gint run;
  79. } XpmSaveInterface;
  80.  
  81.  
  82. typedef struct
  83. {
  84.   guchar r;
  85.   guchar g;
  86.   guchar b;
  87. } rgbkey;
  88.  
  89. /*  whether the image is color or not.  global so I only have to pass
  90.  *  one user value to the GHFunc
  91.  */
  92. gboolean   color;
  93.  
  94. /*  bytes per pixel.  global so I only have to pass one user value
  95.  *  to the GHFunc
  96.  */
  97. gint       cpp;
  98.  
  99. /* Declare local functions */
  100. static void     query               (void);
  101. static void     run                 (gchar         *name,
  102.                      gint           nparams,
  103.                      GimpParam        *param,
  104.                      gint          *nreturn_vals,
  105.                      GimpParam       **return_vals);
  106.  
  107. static gint32   load_image          (gchar         *filename);
  108. static void     parse_colors        (XpmImage      *xpm_image,
  109.                      guchar       **cmap);
  110. static void     parse_image         (gint32         image_ID,
  111.                      XpmImage      *xpm_image,
  112.                      guchar        *cmap);
  113. static gboolean save_image          (gchar         *filename,
  114.                      gint32         image_ID,
  115.                      gint32         drawable_ID);
  116.  
  117. static gint     save_dialog         (void);
  118. static void     save_ok_callback    (GtkWidget     *widget,
  119.                      gpointer       data);
  120.  
  121.  
  122. GimpPlugInInfo PLUG_IN_INFO =
  123. {
  124.   NULL,  /* init_proc  */
  125.   NULL,  /* quit_proc  */
  126.   query, /* query_proc */
  127.   run,   /* run_proc   */
  128. };
  129.  
  130. static XpmSaveVals xpmvals = 
  131. {
  132.   127  /* alpha threshold */
  133. };
  134.  
  135. static XpmSaveInterface xpmint =
  136. {
  137.   FALSE   /*  run  */
  138. };
  139.  
  140.  
  141. MAIN ()
  142.  
  143. static void
  144. query (void)
  145. {
  146.   static GimpParamDef load_args[] =
  147.   {
  148.     { GIMP_PDB_INT32,     "run_mode",     "Interactive, non-interactive" },
  149.     { GIMP_PDB_STRING,    "filename",     "The name of the file to load" },
  150.     { GIMP_PDB_STRING,    "raw_filename", "The name entered" }
  151.   };
  152.   static gint nload_args        = sizeof (load_args) / sizeof (load_args[0]);
  153.  
  154.   static GimpParamDef load_return_vals[] =
  155.   {
  156.     { GIMP_PDB_IMAGE,    "image",         "Output image" }
  157.   };
  158.   static gint nload_return_vals = (sizeof (load_return_vals) /
  159.                    sizeof (load_return_vals[0]));
  160.  
  161.   static GimpParamDef save_args[] =
  162.   {
  163.     { GIMP_PDB_INT32,    "run_mode",      "Interactive, non-interactive" },
  164.     { GIMP_PDB_IMAGE,    "image",         "Input image" },
  165.     { GIMP_PDB_DRAWABLE, "drawable",      "Drawable to save" },
  166.     { GIMP_PDB_STRING,   "filename",      "The name of the file to save the image in" },
  167.     { GIMP_PDB_STRING,   "raw_filename",  "The name of the file to save the image in" },
  168.     { GIMP_PDB_INT32,    "threshold",     "Alpha threshold (0-255)" }
  169.   };
  170.   static gint nsave_args = sizeof (save_args) / sizeof (save_args[0]);
  171.  
  172.   gimp_install_procedure ("file_xpm_load",
  173.                           "loads files of the xpm file format",
  174.                           "FIXME: write help for xpm_load",
  175.                           "Spencer Kimball & Peter Mattis & Ray Lehtiniemi",
  176.                           "Spencer Kimball & Peter Mattis",
  177.                           "1997",
  178.                           "<Load>/Xpm",
  179.                           NULL,
  180.                           GIMP_PLUGIN,
  181.                           nload_args, nload_return_vals,
  182.                           load_args, load_return_vals);
  183.   
  184.   gimp_install_procedure ("file_xpm_save",
  185.                           "saves files in the xpm file format (if you're on a 16 bit display...)",
  186.                           "FIXME: write help for xpm",
  187.                           "Spencer Kimball & Peter Mattis & Ray Lehtiniemi & Nathan Summers",
  188.                           "Spencer Kimball & Peter Mattis",
  189.                           "1997",
  190.                           "<Save>/Xpm",
  191.                           "RGB*, GRAY*, INDEXED*",
  192.                           GIMP_PLUGIN,
  193.                           nsave_args, 0,
  194.                           save_args, NULL);
  195.  
  196.   gimp_register_magic_load_handler ("file_xpm_load",
  197.                     "xpm",
  198.                     "<Load>/Xpm",
  199.                     "0, string,/*\\040XPM\\040*/");
  200.   gimp_register_save_handler ("file_xpm_save",
  201.                   "xpm",
  202.                   "<Save>/Xpm");
  203. }
  204.  
  205. static void
  206. run (gchar   *name,
  207.      gint     nparams,
  208.      GimpParam  *param,
  209.      gint    *nreturn_vals,
  210.      GimpParam **return_vals)
  211. {
  212.   static GimpParam values[2];
  213.   GimpRunModeType  run_mode;
  214.   GimpPDBStatusType   status = GIMP_PDB_SUCCESS;
  215.   gint32        image_ID;
  216.   gint32        drawable_ID;
  217.   GimpExportReturnType export = GIMP_EXPORT_CANCEL;
  218.  
  219.   run_mode = param[0].data.d_int32;
  220.  
  221.   *nreturn_vals = 1;
  222.   *return_vals  = values;
  223.   values[0].type = GIMP_PDB_STATUS;
  224.   values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
  225.  
  226.   if (strcmp (name, "file_xpm_load") == 0)
  227.     {
  228.       INIT_I18N_UI();
  229.  
  230.       image_ID = load_image (param[1].data.d_string);
  231.  
  232.       if (image_ID != -1)
  233.         {
  234.           *nreturn_vals = 2;
  235.           values[1].type         = GIMP_PDB_IMAGE;
  236.           values[1].data.d_image = image_ID;
  237.         }
  238.       else
  239.         {
  240.           status = GIMP_PDB_EXECUTION_ERROR;
  241.         }
  242.     }
  243.   else if (strcmp (name, "file_xpm_save") == 0)
  244.     {
  245.       INIT_I18N_UI();
  246.  
  247.       gimp_ui_init ("xpm", FALSE);
  248.  
  249.       image_ID    = param[1].data.d_int32;
  250.       drawable_ID = param[2].data.d_int32;
  251.  
  252.       /*  eventually export the image */ 
  253.       switch (run_mode)
  254.     {
  255.     case GIMP_RUN_INTERACTIVE:
  256.     case GIMP_RUN_WITH_LAST_VALS:
  257.       export = gimp_export_image (&image_ID, &drawable_ID, "XPM", 
  258.                       (GIMP_EXPORT_CAN_HANDLE_RGB |
  259.                        GIMP_EXPORT_CAN_HANDLE_GRAY |
  260.                        GIMP_EXPORT_CAN_HANDLE_INDEXED |
  261.                        GIMP_EXPORT_CAN_HANDLE_ALPHA ));
  262.       if (export == GIMP_EXPORT_CANCEL)
  263.         {
  264.           values[0].data.d_status = GIMP_PDB_CANCEL;
  265.           return;
  266.         }
  267.       break;
  268.     default:
  269.       break;
  270.     }
  271.  
  272.       switch (run_mode)
  273.     {
  274.     case GIMP_RUN_INTERACTIVE:
  275.       /*  Possibly retrieve data  */
  276.       gimp_get_data ("file_xpm_save", &xpmvals);
  277.  
  278.       /*  First acquire information with a dialog  */
  279.       if (gimp_drawable_has_alpha (drawable_ID))
  280.         if (! save_dialog ())
  281.           status = GIMP_PDB_CANCEL;
  282.       break;
  283.  
  284.     case GIMP_RUN_NONINTERACTIVE:
  285.       /*  Make sure all the arguments are there!  */
  286.       if (nparams != 6)
  287.         {
  288.           status = GIMP_PDB_CALLING_ERROR;
  289.         }
  290.       else
  291.         {
  292.           xpmvals.threshold = param[5].data.d_int32;
  293.  
  294.           if (xpmvals.threshold < 0 ||
  295.           xpmvals.threshold > 255)
  296.         status = GIMP_PDB_CALLING_ERROR;
  297.         }
  298.       break;
  299.  
  300.     case GIMP_RUN_WITH_LAST_VALS:
  301.       /*  Possibly retrieve data  */
  302.       gimp_get_data ("file_xpm_save", &xpmvals);
  303.       break;
  304.  
  305.     default:
  306.       break;
  307.     }
  308.  
  309.       if (status == GIMP_PDB_SUCCESS)
  310.     {
  311.       if (save_image (param[3].data.d_string,
  312.               image_ID,
  313.               drawable_ID))
  314.         {
  315.           gimp_set_data ("file_xpm_save", &xpmvals, sizeof (XpmSaveVals));
  316.         }
  317.       else
  318.         {
  319.           status = GIMP_PDB_EXECUTION_ERROR;
  320.         }
  321.     }
  322.  
  323.       if (export == GIMP_EXPORT_EXPORT)
  324.     gimp_image_delete (image_ID);
  325.     }
  326.   else
  327.     {
  328.       status = GIMP_PDB_CALLING_ERROR;
  329.     }
  330.  
  331.   values[0].data.d_status = status;
  332. }
  333.  
  334.  
  335. static gint32
  336. load_image (gchar *filename)
  337. {
  338.   XpmImage  xpm_image;
  339.   guchar   *cmap;
  340.   gint32    image_ID;
  341.   gchar    *name;
  342.  
  343.   /* put up a progress bar */
  344.   name = g_strdup_printf (_("Loading %s:"), filename);
  345.   gimp_progress_init (name);
  346.   g_free (name);
  347.  
  348.   /* read the raw file */
  349.   XpmReadFileToXpmImage (filename, &xpm_image, NULL);
  350.  
  351.   /* parse out the colors into a cmap */
  352.   parse_colors (&xpm_image, &cmap);
  353.   if (cmap == NULL)
  354.     gimp_quit();
  355.  
  356.   /* create the new image */
  357.   image_ID = gimp_image_new (xpm_image.width,
  358.                              xpm_image.height,
  359.                              GIMP_RGB);
  360.  
  361.   /* name it */
  362.   gimp_image_set_filename (image_ID, filename);
  363.  
  364.   /* fill it */
  365.   parse_image(image_ID, &xpm_image, cmap);
  366.   
  367.   /* clean up and exit */
  368.   g_free(cmap);
  369.   
  370.   return image_ID;
  371. }
  372.  
  373. static void
  374. parse_colors (XpmImage  *xpm_image, 
  375.           guchar   **cmap)
  376. {
  377. #ifndef GDK_WINDOWING_WIN32
  378.   Display  *display;
  379.   Colormap  colormap;
  380. #endif
  381.   gint      i, j;
  382.  
  383. #ifndef GDK_WINDOWING_WIN32
  384.   /* open the display and get the default color map */
  385.   display  = XOpenDisplay (NULL);
  386.   colormap = DefaultColormap (display, DefaultScreen (display));
  387. #endif
  388.     
  389.   /* alloc a buffer to hold the parsed colors */
  390.   *cmap = g_new (guchar, sizeof (guchar) * 4 * xpm_image->ncolors);
  391.  
  392.   if ((*cmap) != NULL)
  393.     {
  394.       /* default to black transparent */
  395.       memset((void*)(*cmap), 0, sizeof (guchar) * 4 * xpm_image->ncolors);
  396.       
  397.       /* parse each color in the file */
  398.       for (i = 0, j = 0; i < xpm_image->ncolors; i++)
  399.         {
  400.           gchar     *colorspec = "None";
  401.           XpmColor *xpm_color;
  402. #ifndef GDK_WINDOWING_WIN32
  403.           XColor    xcolor;
  404. #else
  405.       GdkColor xcolor;
  406. #endif
  407.         
  408.           xpm_color = &(xpm_image->colorTable[i]);
  409.         
  410.           /* pick the best spec available */
  411.           if (xpm_color->c_color)
  412.             colorspec = xpm_color->c_color;
  413.           else if (xpm_color->g_color)
  414.             colorspec = xpm_color->g_color;
  415.           else if (xpm_color->g4_color)
  416.             colorspec = xpm_color->g4_color;
  417.           else if (xpm_color->m_color)
  418.             colorspec = xpm_color->m_color;
  419.         
  420.           /* parse if it's not transparent.  the assumption is that
  421.              g_new will memset the buffer to zeros */
  422.           if (strcmp (colorspec, "None") != 0)
  423.         {
  424. #ifndef GDK_WINDOWING_WIN32
  425.           XParseColor (display, colormap, colorspec, &xcolor);
  426. #else
  427.           gdk_color_parse (colorspec, &xcolor);
  428. #endif
  429.           (*cmap)[j++] = xcolor.red >> 8;
  430.           (*cmap)[j++] = xcolor.green >> 8;
  431.           (*cmap)[j++] = xcolor.blue >> 8;
  432.           (*cmap)[j++] = ~0;
  433.         }
  434.       else
  435.         {
  436.           j += 4;
  437.         }
  438.         }
  439.     }
  440.     
  441. #ifndef GDK_WINDOWING_WIN32
  442.   XCloseDisplay (display);
  443. #endif
  444. }
  445.  
  446. static void
  447. parse_image (gint32    image_ID, 
  448.          XpmImage *xpm_image, 
  449.          guchar   *cmap)
  450. {
  451.   gint       tile_height;
  452.   gint       scanlines;
  453.   gint       val;  
  454.   guchar    *buf;
  455.   guchar    *dest;
  456.   guint     *src;
  457.   GimpPixelRgn  pixel_rgn;
  458.   GimpDrawable *drawable;
  459.   gint32     layer_ID;
  460.   gint       i, j;
  461.     
  462.   layer_ID = gimp_layer_new (image_ID,
  463.                              _("Color"),
  464.                              xpm_image->width,
  465.                              xpm_image->height,
  466.                              GIMP_RGBA_IMAGE,
  467.                              100,
  468.                              GIMP_NORMAL_MODE);
  469.     
  470.   gimp_image_add_layer (image_ID, layer_ID, 0);
  471.  
  472.   drawable = gimp_drawable_get (layer_ID);
  473.     
  474.   gimp_pixel_rgn_init (&pixel_rgn, drawable,
  475.                        0, 0,
  476.                        drawable->width, drawable->height,
  477.                        TRUE, FALSE);
  478.  
  479.   tile_height = gimp_tile_height ();
  480.     
  481.   buf  = g_new (guchar, tile_height * xpm_image->width * 4);
  482.  
  483.   if (buf != NULL)
  484.     {
  485.       src  = xpm_image->data;
  486.       for (i = 0; i < xpm_image->height; i+=tile_height)
  487.         {
  488.           dest = buf;
  489.           scanlines = MIN(tile_height, xpm_image->height - i);
  490.           j = scanlines * xpm_image->width;
  491.           while (j--) {
  492.             {
  493.               val = *(src++) * 4;
  494.               *(dest)   = cmap[val];
  495.               *(dest+1) = cmap[val+1];
  496.               *(dest+2) = cmap[val+2];
  497.               *(dest+3) = cmap[val+3];
  498.               dest += 4;
  499.             }
  500.           
  501.             if ((j % 100) == 0)
  502.               gimp_progress_update ((double) i / (double) xpm_image->height);
  503.           }
  504.         
  505.           gimp_pixel_rgn_set_rect (&pixel_rgn, buf,
  506.                                    0, i,
  507.                                    drawable->width, scanlines);
  508.         
  509.         }
  510.   
  511.       g_free(buf);
  512.     }
  513.  
  514.   gimp_drawable_detach (drawable);
  515. }
  516.  
  517. guint
  518. rgbhash (rgbkey *c)
  519. {
  520.   return ((guint)c->r) ^ ((guint)c->g) ^ ((guint)c->b);
  521. }
  522.  
  523. guint
  524. compare (rgbkey *c1, 
  525.      rgbkey *c2)
  526. {
  527.   return (c1->r == c2->r) && (c1->g == c2->g) && (c1->b == c2->b);
  528. }
  529.     
  530. void
  531. set_XpmImage (XpmColor *array, 
  532.           guint     index, 
  533.           gchar    *colorstring)
  534. {
  535.   gchar *p;
  536.   gint i, charnum, indtemp;
  537.   
  538.   indtemp=index;
  539.   array[index].string = p = g_new (gchar, cpp+1);
  540.   
  541.   /*convert the index number to base sizeof(linenoise)-1 */
  542.   for (i=0; i<cpp; ++i)
  543.     {
  544.       charnum = indtemp % (sizeof (linenoise) - 1);
  545.       indtemp = indtemp / (sizeof (linenoise) - 1);
  546.       *p++ = linenoise[charnum];
  547.     }
  548.   /* *p++=linenoise[indtemp]; */
  549.   
  550.   *p = '\0'; /* C and its stupid null-terminated strings... */
  551.  
  552.   array[index].symbolic = NULL;
  553.   array[index].m_color  = NULL;
  554.   array[index].g4_color = NULL;
  555.  
  556.   if (color)
  557.     {
  558.       array[index].g_color = NULL;
  559.       array[index].c_color = colorstring;
  560.     } else {    
  561.       array[index].c_color = NULL;
  562.       array[index].g_color = colorstring;
  563.     }
  564. }
  565.  
  566. void
  567. create_colormap_from_hash (gpointer gkey, 
  568.                gpointer value, 
  569.                gpointer user_data)
  570. {
  571.   rgbkey *key = gkey;
  572.   gchar *string = g_new(char, 8);
  573.  
  574.   sprintf (string, "#%02X%02X%02X", (int)key->r, (int)key->g, (int)key->b);
  575.   set_XpmImage (user_data, *((int *) value), string);
  576. }
  577.  
  578. static gboolean
  579. save_image (gchar  *filename,
  580.             gint32  image_ID,
  581.             gint32  drawable_ID)
  582. {
  583.   GimpDrawable *drawable;    
  584.  
  585.   gint       width;
  586.   gint       height;
  587.   gint          ncolors = 1;
  588.   gint        *indexno;
  589.   gboolean   indexed;
  590.   gboolean   alpha;
  591.  
  592.   XpmColor  *colormap;
  593.   XpmImage  *image;
  594.  
  595.   guint     *ibuff   = NULL;
  596.   /*guint   *mbuff   = NULL;*/
  597.   GimpPixelRgn  pixel_rgn;
  598.   guchar    *buffer;
  599.   guchar    *data;
  600.  
  601.   GHashTable *hash = NULL;
  602.  
  603.   gint       i, j, k;
  604.   gint       threshold = xpmvals.threshold;
  605.  
  606.   gboolean   rc = FALSE;
  607.  
  608.   /* get some basic stats about the image */
  609.   switch (gimp_drawable_type (drawable_ID)) 
  610.     {
  611.     case GIMP_RGBA_IMAGE:
  612.     case GIMP_INDEXEDA_IMAGE:
  613.     case GIMP_GRAYA_IMAGE:
  614.       alpha = TRUE;
  615.       break;
  616.     case GIMP_RGB_IMAGE:
  617.     case GIMP_INDEXED_IMAGE:
  618.     case GIMP_GRAY_IMAGE:
  619.       alpha = FALSE;
  620.       break;
  621.     default:
  622.       return FALSE;
  623.     }
  624.  
  625.   switch (gimp_drawable_type (drawable_ID)) 
  626.     {
  627.     case GIMP_GRAYA_IMAGE:
  628.     case GIMP_GRAY_IMAGE:
  629.       color = FALSE;
  630.       break;
  631.     case GIMP_RGBA_IMAGE:
  632.     case GIMP_RGB_IMAGE:
  633.     case GIMP_INDEXED_IMAGE:
  634.     case GIMP_INDEXEDA_IMAGE:            
  635.     color = TRUE;
  636.     break;
  637.     default:
  638.       return FALSE;
  639.     }
  640.   
  641.   switch (gimp_drawable_type (drawable_ID)) 
  642.     {
  643.     case GIMP_GRAYA_IMAGE:
  644.     case GIMP_GRAY_IMAGE:
  645.     case GIMP_RGBA_IMAGE:
  646.     case GIMP_RGB_IMAGE:
  647.       indexed = FALSE;
  648.       break;
  649.     case GIMP_INDEXED_IMAGE:
  650.     case GIMP_INDEXEDA_IMAGE:            
  651.       indexed = TRUE;
  652.       break;
  653.     default:
  654.       return FALSE;
  655.     }
  656.   
  657.   drawable = gimp_drawable_get (drawable_ID);
  658.   width    = drawable->width;
  659.   height   = drawable->height;
  660.   
  661.   /* allocate buffers making the assumption that ibuff and mbuff
  662.      are 32 bit aligned... */
  663.   if ((ibuff = g_new (guint, width * height)) == NULL)
  664.     goto cleanup;
  665.  
  666.   /*if ((mbuff = g_new(guint, width*height)) == NULL)
  667.     goto cleanup;*/
  668.   
  669.   if ((hash = g_hash_table_new ((GHashFunc)rgbhash, 
  670.                 (GCompareFunc) compare)) == NULL)
  671.     goto cleanup;
  672.   
  673.   /* put up a progress bar */
  674.   {
  675.     gchar *name;
  676.  
  677.     name = g_strdup_printf (_("Saving %s:"), filename);
  678.     gimp_progress_init (name);
  679.     g_free (name);
  680.   }
  681.  
  682.   
  683.   /* allocate a pixel region to work with */
  684.   if ((buffer = g_new (guchar, 
  685.                gimp_tile_height()*width*drawable->bpp)) == NULL)
  686.     return 0;
  687.  
  688.   gimp_pixel_rgn_init (&pixel_rgn, drawable,
  689.                        0, 0,
  690.                        width, height,
  691.                        TRUE, FALSE);
  692.  
  693.   /* process each row of tiles */
  694.   for (i = 0; i < height; i+=gimp_tile_height())
  695.     {
  696.       gint scanlines;
  697.   
  698.       /* read the next row of tiles */
  699.       scanlines = MIN (gimp_tile_height(), height - i);
  700.       gimp_pixel_rgn_get_rect (&pixel_rgn, buffer, 0, i, width, scanlines);
  701.       data = buffer;
  702.       
  703.       /* process each pixel row */
  704.       for (j=0; j<scanlines; j++)
  705.         {
  706.           /* go to the start of this row in each image */
  707.           guint *idata = ibuff + (i+j) * width;
  708.           /*guint *mdata = mbuff + (i+j) * width;*/
  709.  
  710.           /* do each pixel in the row */
  711.           for (k=0; k<width; k++)
  712.             {
  713.           rgbkey *key = g_new (rgbkey, 1);
  714.           guchar a;
  715.   
  716.               /* get pixel data */
  717.               key->r = *(data++);
  718.           key->g = color && !indexed ? *(data++) : key->r;
  719.           key->b = color && !indexed ? *(data++) : key->r;
  720.           a = alpha ? *(data++) : 255;
  721.           
  722.           if (a < threshold) 
  723.         {
  724.           *(idata++) = 0;
  725.         }
  726.           else
  727.         {
  728.           if (indexed)
  729.             {
  730.               *(idata++) = (key->r)+1;
  731.             }
  732.           else
  733.             {
  734.               indexno = g_hash_table_lookup (hash, key);
  735.               if (!indexno)
  736.             {
  737.               indexno = g_new (gint, 1);
  738.               *indexno = ncolors++;
  739.               g_hash_table_insert (hash, key, indexno);
  740.               key = g_new (rgbkey, 1);
  741.             }
  742.               *(idata++) = *indexno;
  743.             }
  744.         }
  745.             }
  746.  
  747.           /* kick the progress bar */
  748.           gimp_progress_update ((gdouble) (i+j) / (gdouble) height);
  749.         }    
  750.     } 
  751.   g_free (buffer);
  752.  
  753.   if (indexed)
  754.     {
  755.       guchar *cmap;
  756.       cmap = gimp_image_get_cmap(image_ID, &ncolors);
  757.       ncolors++; /* for transparency */
  758.       colormap = g_new (XpmColor, ncolors);
  759.       cpp = (gdouble) 1.0 + 
  760.     (gdouble) log (ncolors) / (double) log (sizeof (linenoise)-1.0);
  761.       set_XpmImage (colormap, 0, "None");
  762.       for (i=0; i<ncolors-1; i++)
  763.     {
  764.       gchar *string;
  765.       guchar r, g, b;
  766.       r = *(cmap++);
  767.       g = *(cmap++);
  768.       b = *(cmap++);
  769.       string = g_new (gchar, 8);
  770.       sprintf (string, "#%02X%02X%02X", (int)r, (int)g, (int)b);
  771.       set_XpmImage (colormap, i+1, string);
  772.     } 
  773.     }
  774.   else
  775.     {
  776.       colormap = g_new (XpmColor, ncolors);
  777.       
  778.       cpp = (gdouble) 1.0 + 
  779.     (gdouble) log (ncolors) / (gdouble) log (sizeof (linenoise) - 1.0);
  780.       set_XpmImage (colormap, 0, "None");
  781.       
  782.       g_hash_table_foreach (hash, create_colormap_from_hash, colormap);
  783.     }
  784.  
  785.   image = g_new (XpmImage, 1);
  786.   
  787.   image->width=width;
  788.   image->height=height;
  789.   image->ncolors=ncolors;
  790.   image->cpp=cpp;
  791.   image->colorTable=colormap;        
  792.   image->data = ibuff;
  793.   
  794.   /* do the save */
  795.   rc = (XpmWriteFileFromXpmImage (filename, image, NULL) == XpmSuccess);
  796.  
  797.  cleanup:
  798.   /* clean up resources */  
  799.   gimp_drawable_detach (drawable);
  800.   
  801.   if (ibuff) 
  802.     g_free (ibuff);
  803.   /*if (mbuff) g_free(mbuff);*/
  804.   if (hash)  
  805.     g_hash_table_destroy (hash);
  806.   
  807.   return rc;
  808. }
  809.  
  810. static gint
  811. save_dialog (void)
  812. {
  813.   GtkWidget *dlg;
  814.   GtkWidget *frame;
  815.   GtkWidget *table;
  816.   GtkObject *scale_data;
  817.  
  818.   dlg = gimp_dialog_new (_("Save as XPM"), "xpm",
  819.              gimp_standard_help_func, "filters/xpm.html",
  820.              GTK_WIN_POS_MOUSE,
  821.              FALSE, TRUE, FALSE,
  822.  
  823.              _("OK"), save_ok_callback,
  824.              NULL, NULL, NULL, TRUE, FALSE,
  825.              _("Cancel"), gtk_widget_destroy,
  826.              NULL, 1, NULL, FALSE, TRUE,
  827.  
  828.              NULL);
  829.  
  830.   gtk_signal_connect (GTK_OBJECT (dlg), "destroy",
  831.               GTK_SIGNAL_FUNC (gtk_main_quit),
  832.               NULL);
  833.  
  834.   /*  parameter settings  */
  835.   frame = gtk_frame_new (_("Parameter Settings"));
  836.   gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
  837.   gtk_container_set_border_width (GTK_CONTAINER (frame), 6);
  838.   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->vbox), frame, TRUE, TRUE, 0);
  839.   gtk_widget_show (frame);
  840.  
  841.   table = gtk_table_new (1, 3, FALSE);
  842.   gtk_table_set_col_spacings (GTK_TABLE (table), 4);
  843.   gtk_container_set_border_width (GTK_CONTAINER (table), 4);
  844.   gtk_container_add (GTK_CONTAINER (frame), table);
  845.   gtk_widget_show (table);
  846.  
  847.   scale_data = gimp_scale_entry_new (GTK_TABLE (table), 0, 0,
  848.                      _("Alpha Threshold:"), SCALE_WIDTH, 0,
  849.                      xpmvals.threshold, 0, 255, 1, 8, 0,
  850.                      TRUE, 0, 0,
  851.                      NULL, NULL);
  852.  
  853.   gtk_signal_connect (GTK_OBJECT (scale_data), "value_changed",
  854.               GTK_SIGNAL_FUNC (gimp_int_adjustment_update),
  855.               &xpmvals.threshold);
  856.  
  857.   gtk_widget_show (dlg);
  858.  
  859.   gtk_main ();
  860.   gdk_flush ();
  861.  
  862.   return xpmint.run;
  863. }
  864.  
  865. static void
  866. save_ok_callback (GtkWidget *widget,
  867.           gpointer   data)
  868. {
  869.   xpmint.run = TRUE;
  870.  
  871.   gtk_widget_destroy (GTK_WIDGET (data));
  872. }
  873.